﻿// ———————————————————————–
// <copyright file="RouterRulesDAL.cs" company="The MITRE Corporation">
//    Copyright (c) 2010 The MITRE Corporation. All rights reserved.
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
// </copyright>
// ———————————————————————–
/////////////////////////////////////////////////////////////////////////////////////////////////
// RouterRulesDAL.cs - Data Access Layer For the Rules to Be Used To Route EDXL-DE Messages
// Project: IC.NETSOA- IC.NETDAL
//
// Language:    C#, .NET 4.0
// Platform:    Dell XPS m1530 Windows 7, VS 2010
// Author:      Don McGarry MITRE Rome
/////////////////////////////////////////////////////////////////////////////////////////////////

// Copyright (c) 2010 The MITRE Corporation. All rights reserved.
//
// NOTICE
// This software was produced for the U. S. Government
// under Contract No. FA8721-09-C-0001, and is
// subject to the Rights in Noncommercial Computer Software
// and Noncommercial Computer Software Documentation Clause
// (DFARS) 252.227-7014 (JUN 1995)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using EDXLSharp;
using ICNETServices;

namespace ICNETDAL
{
  /// <summary>
  /// This class contains methods that are used to retrieve and modify the rules that route EDXL-DE messages. 
  /// </summary>
  public static class RouterRulesDAL
  {
    #region Public Member Functions

    /// <summary>
    /// Gets the routing destintaions for a rule in a set of value lists.
    /// </summary>
    /// <param name="type">The rule that corresponds to the routing destinations.</param>
    /// <param name="values">A list of ValueLists where the rule can be found.</param>
    /// <returns>A list of ValueLists, where each ValueList a route and it's correpsonding URI.</returns>
    public static List<ValueList> GetRoutingDestinations(string type, List<ValueList> values)
    {
      ICNETRouterRulesDataContext routerdb = new ICNETRouterRulesDataContext(ICNETSOAConstants.RouterRulesConnectionString);
      List<ValueList> destinationroutes = new List<ValueList>();
      ValueList tmplist;
      bool found;
      foreach (ValueList fromlist in values)
      {
        foreach (string fromvalue in fromlist.Value)
        {
          var routes = from p in routerdb.Rules
                        where p.RuleType == type &&
                        p.FromListURI == fromlist.ValueListURN &&
                        p.FromListValue == fromvalue
                        select p;
          foreach (Rule route in routes)
          {
            found = false;
            foreach (ValueList list in destinationroutes)
            {
              if (list.ValueListURN == route.ToListURI)
              {
                found = true;

                //don't add duplicate value to the list
                //just a new one
                if (!list.Value.Contains(route.ToListValue))
                {
                  list.Value.Add(route.ToListValue);
                }
                break;
              }
            }

            if (!found)
            {
              tmplist = new ValueList(route.ToListURI, new List<string> { route.ToListValue });
              destinationroutes.Add(tmplist);
            }
          }
        }
      }

      return destinationroutes;
    }

    /// <summary>
    /// Gets all forwarding destinations that the federation service forwards to.
    /// </summary>
    /// <param name="type">The rule that corresponds to the routing destinations.</param>
    /// <param name="values">A list of ValueLists where the rule can be found.</param>
    /// <returns>A list of strings, where each string is one destination</returns>
    public static List<string> GetDistinctFederationDestinations(string type, List<ValueList> values)
    {
      ICNETRouterRulesDataContext routerdb = new ICNETRouterRulesDataContext(ICNETSOAConstants.RouterRulesConnectionString);
      List<string> destinationroutes = new List<string>();
      foreach (ValueList fromlist in values)
      {
        foreach (string fromvalue in fromlist.Value)
        {
          var routes = from p in routerdb.Rules
                       where (p.RuleType == type &&
                       (p.FromListURI == fromlist.ValueListURN &&
                       p.FromListValue == fromvalue)) ||
                       p.FromListURI == "*"
                       select p;
          foreach (Rule route in routes)
          {
            destinationroutes.Add(route.ToListURI);
          }
        }
      }

      return destinationroutes.Distinct().ToList();
    }

    /// <summary>
    /// Queries the RouterRules database and returns all the router rules currently in the database 
    /// </summary>
    /// <returns>an XML object containing all of the routing rules.</returns>
    public static XElement GetRouterRules()
    {
      ICNETRouterRulesDataContext routerdb = new ICNETRouterRulesDataContext(ICNETSOAConstants.RouterRulesConnectionString);
      XElement xe = new XElement("RouterRules");
      var routes = from r in routerdb.Rules select r;

      foreach (Rule r in routes)
      {
        xe.Add(new XElement("RouterRule", new XElement("RuleKey", r.RuleKey), new XElement("RuleType", r.RuleType), new XElement("FromListURI", r.FromListURI), new XElement("FromListValue", r.FromListValue), new XElement("ToListURI", r.ToListURI), new XElement("ToListValue", r.ToListValue)));
      }

      return xe;
    }

    /// <summary>
    /// Takes a GUID and looks up the corresponding router rule.
    /// </summary>
    /// <param name="guid">a globallly unique identifire that corresponds to a value list</param>
    /// <returns>the router rule</returns>
    public static XElement GetRouterRule(System.Guid guid)
    {
      ICNETRouterRulesDataContext routerdb = new ICNETRouterRulesDataContext(ICNETSOAConstants.RouterRulesConnectionString);
      var rl = from rs in routerdb.Rules where rs.RuleKey == guid select rs;
      if ((rl.Count() > 1) || (rl.Count() < 1))
      {
        return new XElement("RouterRule", null);
      }

      Rule r = rl.First(); 
      return new XElement("RouterRule", new XElement("RuleKey", r.RuleKey), new XElement("RuleType", r.RuleType), new XElement("FromListURI", r.FromListURI), new XElement("FromListValue", r.FromListValue), new XElement("ToListURI", r.ToListURI), new XElement("ToListValue", r.ToListValue));
    }

    /// <summary>
    /// Creates a routing rule from the RouterRules Database
    /// </summary>
    /// <param name="ruleXML">The routing rule, in XML format, that should be added to the database</param>
    /// <returns> IF the inputted rule was properly formatted, it will be returned as an XML object with a new GUID
    /// If the inputted rule is improperly formatted, then an XML object will be returned, but not added to the database</returns>
    public static XElement CreateRouterRule(XElement ruleXML)
    {
      ICNETRouterRulesDataContext routerdb = new ICNETRouterRulesDataContext(ICNETSOAConstants.RouterRulesConnectionString);
      try
      {
        string type = ruleXML.Descendants("RuleType").First().Value;
        string furi = ruleXML.Descendants("FromListURI").First().Value;
        string fval = ruleXML.Descendants("FromListValue").First().Value;
        string turi = ruleXML.Descendants("ToListURI").First().Value;
        string tval = ruleXML.Descendants("ToListValue").First().Value;

        if ((type == string.Empty) || (furi == string.Empty) || (fval == string.Empty) || (turi == string.Empty) || (tval == string.Empty))
        {
          return new XElement("RouterRuleError", "Type, FromListURI, FromListValue, ToListURI, and ToListValue cannot be blank.");
        }

        var rules = from rl in routerdb.Rules 
                    where rl.RuleType == type &&
                       rl.FromListURI == furi && 
                       rl.FromListValue == fval &&
                       rl.ToListURI == turi &&
                       rl.ToListValue == tval
                     select rl;
        if (rules.Count() > 0)
        {
          return new XElement("RouterRule", new XElement("RuleKey", rules.First().RuleKey), new XElement("RuleType", rules.First().RuleType), new XElement("FromListURI", rules.First().FromListURI), new XElement("FromListValue", rules.First().FromListValue), new XElement("ToListURI", rules.First().ToListURI), new XElement("ToListValue", rules.First().ToListValue));
        }

        Rule r = new Rule { RuleKey = System.Guid.NewGuid(), RuleType = type, FromListURI = furi, FromListValue = fval, ToListURI = turi, ToListValue = tval };
        routerdb.Rules.InsertOnSubmit(r);
        routerdb.SubmitChanges();
        return new XElement("RouterRule", new XElement("RuleKey", r.RuleKey), new XElement("RuleType", r.RuleType), new XElement("FromListURI", r.FromListURI), new XElement("FromListValue", r.FromListValue), new XElement("ToListURI", r.ToListURI), new XElement("ToListValue", r.ToListValue));
      }
      catch (InvalidOperationException) 
      { 
        return new XElement("RouterRuleError", "Type, FromListURI, FromListValue, ToListURI, and ToListValue must all be specified"); 
      }
    }

    /// <summary>
    /// Modifies a routing rule from the RouterRules Database
    /// </summary>
    /// <param name="ruleXML">The routing rule, in XML format, that should be modified in the database</param>
    /// <returns> IF the inputted rule was properly formatted, it will be returned as an XML object with a new GUID
    /// If the inputted rule is improperly formatted, then an XML object will be returned, but not added to the database</returns>


    public static XElement ModifyRouterRule(XElement ruleXML, System.Guid guid)
    {
        ICNETRouterRulesDataContext routerdb = new ICNETRouterRulesDataContext(ICNETSOAConstants.RouterRulesConnectionString);

        var modrule = from rs in routerdb.Rules where rs.RuleKey == guid select rs;
        try
        {
            string type = ruleXML.Descendants("RuleType").First().Value;
            string furi = ruleXML.Descendants("FromListURI").First().Value;
            string fval = ruleXML.Descendants("FromListValue").First().Value;
            string turi = ruleXML.Descendants("ToListURI").First().Value;
            string tval = ruleXML.Descendants("ToListValue").First().Value;

            if ((type == string.Empty) || (furi == string.Empty) || (fval == string.Empty) || (turi == string.Empty) || (tval == string.Empty))
            {
                return new XElement("RouterRuleError", "Type, FromListURI, FromListValue, ToListURI, and ToListValue cannot be blank.");
            }

            Rule r = modrule.First();

            r.RuleType = type;
            r.FromListURI = furi;
            r.FromListValue = fval;
            r.ToListURI = turi;
            r.ToListValue = tval;
            
            routerdb.SubmitChanges();
            return new XElement("RouterRule", new XElement("RuleKey", r.RuleKey), new XElement("RuleType", r.RuleType), new XElement("FromListURI", r.FromListURI), new XElement("FromListValue", r.FromListValue), new XElement("ToListURI", r.ToListURI), new XElement("ToListValue", r.ToListValue));

        }
        catch (InvalidOperationException)
        {
            return new XElement("RouterRuleError", "Type, FromListURI, FromListValue, ToListURI, and ToListValue must all be specified");
        }
       
    }

    /// <summary>
    /// Deletes a routing rule from the RouterRules Database
    /// </summary>
    /// <param name="guid">The GUID of the routing rule that should be deleted</param>
    public static void DeleteRouterRule(System.Guid guid)
    {
      ICNETRouterRulesDataContext routerdb = new ICNETRouterRulesDataContext(ICNETSOAConstants.RouterRulesConnectionString);
      var rules = from rl in routerdb.Rules where rl.RuleKey == guid select rl;
      if (rules.Count() < 1)
      {
        return;
      }

      routerdb.Rules.DeleteOnSubmit(rules.First());
      routerdb.SubmitChanges();
    }

    /// <summary>
    /// Determines Valid Feed Values from the Rules Database
    /// </summary>
    /// <returns>List of ValueKey Pairs</returns>
    public static VLList GetAvailableFeedsFromRules()
    {
      VLList feedkeys = new VLList();
      ICNETRouterRulesDataContext routerdb = new ICNETRouterRulesDataContext(ICNETSOAConstants.RouterRulesConnectionString);
      XElement xe = new XElement("RouterRules");
      var routes = from r in routerdb.Rules select r;
      ValueList temp;

      foreach (Rule r in routes)
      {
        if (r.RuleType != "ContentKeyword")
        {
          continue;
        }

        if (!feedkeys.Contains(r.ToListURI))
        {
          feedkeys.Add(new ValueList(r.ToListURI, new List<string>() { r.ToListValue }));
        }
        else
        {
          temp = feedkeys.GetValueList(r.ToListURI);
          if (!temp.Value.Contains(r.ToListValue))
          {
            temp.Value.Add(r.ToListValue);
          }
        }
      }

      return feedkeys;
    }

    #endregion

    #region Protected Member Functions

    #endregion

    #region Private Member Functions

    #endregion         
  }
}
